package com.serialPort.web.service.impl;

import com.fazecast.jSerialComm.SerialPort;
import com.fazecast.jSerialComm.SerialPortDataListener;
import com.fazecast.jSerialComm.SerialPortEvent;
import com.serialPort.web.service.ASTMMessageService;
import com.serialPort.web.service.SerialPortService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantLock;

@Slf4j
@Service
public class ASTMMessageServiceImpl implements ASTMMessageService {

    @Value("${lisapi.sleepTime}")
    private Integer sleepTime;

    @Value("${lisapi.com}")
    private String comPort;

    @Value("${lisapi.baudrate}")
    private Integer baudrate;

    @Autowired
    private SerialPortService serialPortService;

    private ReentrantLock lock = new ReentrantLock();
    private long lastReadTime = System.currentTimeMillis();
    private Timer timeoutTimer;

    // ASTM协议状态变量
    private final StringBuilder frameBuffer = new StringBuilder();
    private final StringBuilder bccBuffer = new StringBuilder();
    private final AtomicBoolean inTransaction = new AtomicBoolean(false);
    private final AtomicBoolean inFrame = new AtomicBoolean(false);
    private final AtomicBoolean awaitingBCC = new AtomicBoolean(false);
    private SerialPort currentSerialPort;

    private final StringBuilder messageHl7_str = new StringBuilder();

    /**
     * 启动ASTM协议监听
     */
    @Override
    public void startASTMListener(SerialPort serialPort) {
        this.currentSerialPort = serialPort;
        configureSerialPort(serialPort);

        if (serialPort.openPort()) {
            log.info("✅ 成功打开ASTM串口: {}", serialPort.getSystemPortName());

            serialPort.addDataListener(new SerialPortDataListener() {
                @Override
                public int getListeningEvents() {
                    return SerialPort.LISTENING_EVENT_DATA_RECEIVED;
                }

                @Override
                public void serialEvent(SerialPortEvent event) {
                    if (event.getEventType() != SerialPort.LISTENING_EVENT_DATA_RECEIVED) {
                        return;
                    }

                    try {
                        byte[] receivedData = event.getReceivedData();
                        if (receivedData != null && receivedData.length > 0) {
                            processASTMProtocol(receivedData, serialPort);
                        }
                    } catch (Exception e) {
                        log.error("ASTM数据接收异常", e);
                    }
                }
            });

        } else {
            log.error("❌ 打开ASTM串口失败: {}", serialPort.getSystemPortName());
        }
    }

    /**
     * 配置串口参数
     */
    private void configureSerialPort(SerialPort serialPort) {
        serialPort.setBaudRate(baudrate);
        serialPort.setNumDataBits(7);  // ASTM通常使用7数据位
        serialPort.setNumStopBits(1);
        serialPort.setParity(SerialPort.EVEN_PARITY);  // ASTM使用偶校验
        serialPort.setFlowControl(SerialPort.FLOW_CONTROL_DISABLED);
        serialPort.setComPortTimeouts(SerialPort.TIMEOUT_READ_SEMI_BLOCKING, 100, 0);
    }

    /**
     * ASTM协议处理器 - 完整版本
     */
    private void processASTMProtocol(byte[] data, SerialPort serialPort) {
        if (timeoutTimer != null) {
            timeoutTimer.cancel();
            timeoutTimer.purge();
        }

        // 或者指定其他编码
        String utf8String = new String(data, StandardCharsets.UTF_8);
//        String asciiString = new String(data, StandardCharsets.US_ASCII);
//        String isoString = new String(data, StandardCharsets.ISO_8859_1);

        log.info("开始处理ASTM数据，长度: {} 字节", data.length);
        log.info("获取到的数据为utf-8：{}" , utf8String);
//        log.info("US_ASCII：" + asciiString);
//        log.info("iso_8859_1:" + isoString);


        for (int i = 0; i < data.length; i++) {
            byte currentByte = data[i];
            processSingleByte(currentByte, serialPort);
        }

        if (!"\u0004".equals(utf8String)){
            lock.lock();
            try {
                resetTimeoutTimer(serialPort);
            } finally {
                lock.unlock();
            }
        }

        messageHl7_str.append(frameBuffer.toString()).append("\n");
    }

    /**
     * 处理单个字节
     */
    private void processSingleByte(byte b, SerialPort serialPort) {
        switch (b) {
            case 0x05: // <ENQ> - 查询
                handleENQ(serialPort);
                break;

            case 0x02: // <STX> - 文本开始
                handleSTX();
                break;

            case 0x03: // <ETX> - 文本结束
                handleETX();
                break;

            case 0x04: // <EOT> - 传输结束
                handleEOT(serialPort);
                break;

            case 0x06: // <ACK> - 确认
                handleACK();
                break;

            case 0x15: // <NAK> - 否定确认
                handleNAK();
                break;

            case 0x0D: // <CR> - 回车
                handleCR(serialPort);
                break;

            case 0x0A: // <LF> - 换行
                handleLF();
                break;

            default:
                handleDataByte((char) b, serialPort);
                break;
        }
    }

    /**
     * 处理ENQ字符
     */
    private void handleENQ(SerialPort serialPort) {
        log.info("🔔 接收到 ENQ，开始ASTM事务");
        sendACK(serialPort);
        inTransaction.set(true);
        resetBuffers();
    }

    /**
     * 处理STX字符
     */
    private void handleSTX() {
        log.info("📨 接收到 STX，开始接收帧数据");
        frameBuffer.setLength(0);
        inFrame.set(true);
        awaitingBCC.set(false);
    }

    /**
     * 处理ETX字符
     */
    private void handleETX() {
        log.info("📬 接收到 ETX，等待BCC校验字符");
        inFrame.set(false);
        awaitingBCC.set(true);
        bccBuffer.setLength(0); // 清空BCC缓冲区
    }

    /**
     * 处理EOT字符
     */
    private void handleEOT(SerialPort serialPort) {
        log.info("🏁 接收到 EOT/超时触发，ASTM事务结束");
        inTransaction.set(false);
        resetBuffers();
        log.info("开始处理hl7数据：" + messageHl7_str);
        String result = serialPortService.AnalysisUploadCS1200Message(String.valueOf(messageHl7_str));
        messageHl7_str.setLength(0);
        if ("200".equals(result)) {
            sendACK(serialPort);
        } else {
            sendNAK(serialPort);
        }
    }

    /**
     * 处理ACK字符
     */
    private void handleACK() {
        log.info("✅ 接收到 ACK");
        // ACK通常由对方发送，确认我们之前发送的数据
    }

    /**
     * 处理NAK字符
     */
    private void handleNAK() {
        log.warn("❌ 接收到 NAK，对方否定确认");
        // 可能需要重传数据或处理错误
    }

    /**
     * 处理CR字符
     */
    private void handleCR(SerialPort serialPort) {
        if (awaitingBCC.get() && bccBuffer.length() == 2) {
            log.info("↵ 接收到 CR，完成BCC接收");
            verifyAndProcessFrame(serialPort);
        }
    }

    /**
     * 处理LF字符
     */
    private void handleLF() {
        // ASTM协议中LF通常与CR配对使用，可以忽略或记录
        log.debug("↓ 接收到 LF");
    }

    /**
     * 处理数据字节
     */
    private void handleDataByte(char c, SerialPort serialPort) {
        if (inFrame.get()) {
            // 帧数据内容
            frameBuffer.append(c);
        } else if (awaitingBCC.get()) {
            // BCC校验字符
            processBCCCharacter(c, serialPort);
        } else {
            log.warn("接收到未知数据字符: '{}' (0x{})", c, Integer.toHexString(c));
        }
    }

    /**
     * 处理BCC字符
     */
    private void processBCCCharacter(char bccChar, SerialPort serialPort) {
        // 只接受十六进制字符
        if (isHexCharacter(bccChar)) {
            if (bccBuffer.length() < 2) {
                bccBuffer.append(bccChar);
                log.debug("添加到BCC缓冲: '{}'", bccChar);
            }

            if (bccBuffer.length() == 2) {
                verifyAndProcessFrame(serialPort);
            }
        } else {
            log.warn("无效的BCC字符: '{}'", bccChar);
        }
    }

    /**
     * 验证BCC并处理帧
     */
    private void verifyAndProcessFrame(SerialPort serialPort) {
        String receivedBCC = bccBuffer.toString();
        String frameData = frameBuffer.toString();

        log.info("验证BCC: 数据='{}', 接收BCC={}", frameData, receivedBCC);

        if (verifyBCC(frameData, receivedBCC)) {
            log.info("✅ BCC校验通过");
        }
        else {
            String expectedBCC = calculateBCCHex(frameData);
            log.info("❌ BCC校验失败，期望: {}, 实际: {},但继续执行", expectedBCC, receivedBCC);
        }

        processASTMFrame(frameData, serialPort);
        sendACK(serialPort);
//        else {
//            String expectedBCC = calculateBCCHex(frameData);
//            log.error("❌ BCC校验失败，期望: {}, 实际: {}", expectedBCC, receivedBCC);
//            sendNAK(serialPort);
//        }

        // 重置状态
        awaitingBCC.set(false);
        bccBuffer.setLength(0);
    }

    /**
     * 处理ASTM数据帧
     */
    private void processASTMFrame(String frameData, SerialPort serialPort) {
        try {
            if (frameData.length() < 2) {
                log.warn("帧数据过短: {}", frameData);
                return;
            }

            String frameType = frameData.substring(0, 2);
            String frameContent = frameData.length() > 2 ? frameData.substring(2) : "";

            log.info("🎯 处理ASTM帧 - 类型: {}, 内容: {}", frameType, frameContent);

            // 根据帧类型分发处理
            switch (frameType) {
                case "0O":
                    processQueryFrame(frameContent);
                    break;
                case "1H":
                    processHeaderFrame(frameContent);
                    break;
                case "2P":
                    processPatientFrame(frameContent);
                    break;
                case "3O":
                    processOrderFrame(frameContent);
                    break;
                case "4R":
                    processResultFrame(frameContent);
                    break;
                case "5L":
                    processQualityControlFrame(frameContent);
                    break;
                case "6H":
                    processInstrumentStatusFrame(frameContent);
                    break;
                case "7P":
                    processPatientExtendedFrame(frameContent);
                    break;
                case "8C":
                    processCommentFrame(frameContent);
                    break;
                case "9L":
                    processTerminatorFrame(frameContent);
                    break;
                default:
                    log.warn("未知帧类型: {}", frameType);
                    break;
            }

        } catch (Exception e) {
            log.error("处理ASTM帧异常", e);
        }
    }

    // ASTM帧处理方法（实现具体的业务逻辑）
    private void processQueryFrame(String content) {
        log.info("处理查询帧: {}", content);
        // 实现查询帧处理逻辑
    }

    private void processHeaderFrame(String content) {
        log.info("处理头帧: {}", content);
        // 实现头帧处理逻辑
    }

    private void processPatientFrame(String content) {
        log.info("处理患者帧: {}", content);
        // 实现患者帧处理逻辑
    }

    private void processOrderFrame(String content) {
        log.info("处理订单帧: {}", content);
        // 实现订单帧处理逻辑
    }

    private void processResultFrame(String content) {
        log.info("处理结果帧: {}", content);
        // 实现结果帧处理逻辑
    }

    private void processQualityControlFrame(String content) {
        log.info("处理质控帧: {}", content);
        // 实现质控帧处理逻辑
    }

    private void processInstrumentStatusFrame(String content) {
        log.info("处理仪器状态帧: {}", content);
        // 实现仪器状态处理逻辑
    }

    private void processPatientExtendedFrame(String content) {
        log.info("处理患者扩展帧: {}", content);
        // 实现患者扩展信息处理逻辑
    }

    private void processCommentFrame(String content) {
        log.info("处理注释帧: {}", content);
        // 实现注释处理逻辑
    }

    private void processTerminatorFrame(String content) {
        log.info("处理结束帧: {}", content);
        // 实现结束帧处理逻辑
    }

    /**
     * 工具方法
     */
    private String getCharRepresentation(byte b) {
        if (b == 0x05) return "<ENQ>";
        if (b == 0x02) return "<STX>";
        if (b == 0x03) return "<ETX>";
        if (b == 0x04) return "<EOT>";
        if (b == 0x06) return "<ACK>";
        if (b == 0x15) return "<NAK>";
        if (b == 0x0D) return "<CR>";
        if (b == 0x0A) return "<LF>";
        if (b >= 32 && b <= 126) return "'" + (char) b + "'";
        return "---";
    }

    private boolean isHexCharacter(char c) {
        return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f');
    }

    private void resetBuffers() {
        frameBuffer.setLength(0);
        bccBuffer.setLength(0);
        inFrame.set(false);
        awaitingBCC.set(false);
    }

    private boolean verifyBCC(String frameData, String receivedBCC) {
        String calculatedBCC = calculateBCCHex(frameData);
        return calculatedBCC.equalsIgnoreCase(receivedBCC);
    }

    private String calculateBCCHex(String data) {
        byte bcc = calculateBCC(data);
        return String.format("%02X", bcc);
    }

    private byte calculateBCC(String data) {
        byte bcc = 0;
        for (byte b : data.getBytes(StandardCharsets.UTF_8)) {
            bcc ^= b;
        }
        return bcc;
    }

    private void sendACK(SerialPort serialPort) {
        sendControlCharacter(serialPort, (byte) 0x06, "ACK");
    }

    private void sendNAK(SerialPort serialPort) {
        sendControlCharacter(serialPort, (byte) 0x15, "NAK");
    }

    private void sendControlCharacter(SerialPort serialPort, byte controlChar, String charName) {
        try {
            byte[] data = { controlChar };
            serialPort.writeBytes(data, data.length);
            log.info("📤 发送 {}", charName);
        } catch (Exception e) {
            log.error("发送{}失败", charName, e);
        }
    }

    /**
     * 停止监听
     */
    public void stopListener() {
        if (currentSerialPort != null && currentSerialPort.isOpen()) {
            currentSerialPort.closePort();
            log.info("🛑 ASTM监听器已停止");
        }
        resetBuffers();
        inTransaction.set(false);
    }


    /**
     * 重置超时时间
     */
    private void resetTimeoutTimer(SerialPort serialPort) {
        if (timeoutTimer != null) {
            timeoutTimer.cancel();
            timeoutTimer.purge();
        }

        timeoutTimer = new Timer();
        timeoutTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                log.info("超时，未获取到结束字段，自动清空！！！");
                messageHl7_str.setLength(0);
            }
        }, sleepTime);
    }
}
